package com.xh.hongaicodegenerator.ai.tools;

import cn.hutool.json.JSONObject;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolMemoryId;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

import static com.xh.hongaicodegenerator.langgraph4j.node.CodeGeneratorNode.escapeCurlyWithHint;

/**
 * NPM构建工具
 * 支持AI通过工具调用的方式执行npm run build命令
 */
@Slf4j
@Component
public class NpmBuildTool extends BaseTool {

    @Tool("执行 npm run build 命令构建Vue项目")
    public String buildProject(@ToolMemoryId Long appId) {
        try {
            // 构建项目目录路径
            String projectDirName = "vue_project_" + appId;
            String projectRootPath = System.getProperty("user.dir") + File.separator +
                    "tmp" + File.separator + "code_output" + File.separator + projectDirName;
            File projectDir = new File(projectRootPath).getCanonicalFile();

            if (!projectDir.exists() || !projectDir.isDirectory()) {
                return "错误：项目不存在";
            }

            // 检查package.json是否存在
            File packageJsonFile = new File(projectDir, "package.json");
            if (!packageJsonFile.exists()) {
                return "错误：项目目录中没有找到 package.json 文件";
            }

            log.info("在目录 {} 中执行 npm run build 命令", projectDir.getAbsolutePath());

            // 根据操作系统选择正确的npm命令
            String[] command = getNpmCommand("run", "build");

            // 执行 npm run build 命令并捕获输出
            Process process = Runtime.getRuntime().exec(command, null, projectDir);

            // 读取标准输出和错误输出

            StringBuilder errorOutput = new StringBuilder();

            // 读取标准输出
            BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));


            Thread errorThread = new Thread(() -> {
                try {
                    String line;
                    while ((line = stdError.readLine()) != null) {
                        errorOutput.append(line).append("\n");
                    }
                } catch (Exception e) {
                    log.error("读取错误输出时出错: {}", e.getMessage());
                }
            });

            errorThread.start();

            // 等待进程完成，设置超时时间（5分钟）
            boolean finished = process.waitFor(300, TimeUnit.SECONDS);
            if (!finished) {
                log.error("npm run build 命令执行超时，强制终止进程");
                process.destroyForcibly();
                return "错误：npm run build 命令执行超时，检查代码是否有误";
            }

            errorThread.join(5000);  // 等待最多5秒

            int exitCode = process.exitValue();

            if (exitCode == 0) {
                log.info("npm run build 命令执行成功");
                // 检查是否生成了dist目录
                File distDir = new File(projectDir, "dist");
                if (distDir.exists() && distDir.isDirectory()) {
                    return "项目构建成功";
                } else {
                    return "警告：构建成功但未找到dist目录";
                }
            } else {
                log.error("npm run build 命令执行失败，退出码: {}", exitCode);
                // 构建失败时返回详细信息
                String errorResult = errorOutput.toString();
                // 过滤和脱敏错误信息
                String filteredError = filterAndDesensitizeError(errorResult, projectRootPath);

                StringBuilder result = new StringBuilder();
                result.append("构建失败，退出码: ").append(exitCode).append("\n");
                result.append("错误输出:\n").append(filteredError).append("\n");

                return result.toString();
            }
        } catch (Exception e) {
            String errorMessage = "执行npm run build命令失败: " + e.getMessage();
            log.error(errorMessage, e);
            return escapeCurlyWithHint(errorMessage);
        }
    }

    /**
     * 过滤和脱敏错误信息
     * @param errorOutput 错误输出
     * @param projectRootPath 项目根路径
     * @return 过滤和脱敏后的错误信息
     */
    private String filterAndDesensitizeError(String errorOutput, String projectRootPath) {
        if (errorOutput == null || errorOutput.isEmpty()) {
            return "";
        }

        // 将项目根路径转换为Linux格式（正斜杠）
        String linuxProjectRootPath = projectRootPath.replace("\\", "/");

        // 使用replace将项目根路径替换为空字符串，实现脱敏
        String filteredOutput = errorOutput.replace(linuxProjectRootPath + "/", "");

        // 按行分割并过滤掉以 "at " 开头的堆栈跟踪行
        String[] lines = filteredOutput.split("\n");
        StringBuilder result = new StringBuilder();

        // 标记是否已找到"error during build:"行
        boolean foundErrorLine = false;

        for (String line : lines) {
            // 跳过以 "at " 开头的堆栈跟踪行
            if (line.trim().startsWith("at ")) {
                continue;
            }

            // 如果找到了"error during build:"行，则开始记录
            if (line.contains("error during build:")) {
                foundErrorLine = true;
            }

            // 如果已找到错误行或者这是错误行本身，则添加到结果中
            if (foundErrorLine) {
                result.append(line).append("\n");
            }
        }

        // 如果没有找到"error during build:"行，则返回处理后的全部内容（但仍然过滤掉at行）
        if (!foundErrorLine) {
            return result.toString().trim();
        }

        return escapeCurlyWithHint(result.toString().trim());
    }

    /**
     * 根据操作系统返回正确的npm命令
     * @param args npm命令参数
     * @return 正确的命令数组
     */
    private String[] getNpmCommand(String... args) {
        String npmCommand = isWindows() ? "npm.cmd" : "npm";
        String[] command = new String[1 + args.length];
        command[0] = npmCommand;
        System.arraycopy(args, 0, command, 1, args.length);
        return command;
    }

    /**
     * 判断是否为Windows系统
     * @return 是否为Windows系统
     */
    private boolean isWindows() {
        String osName = System.getProperty("os.name").toLowerCase();
        return osName.contains("windows");
    }

    @Override
    public String getToolName() {
        return "buildProject";
    }

    @Override
    public String getDisplayName() {
        return "构建项目";
    }

    @Override
    public String generateToolExecutedResult(JSONObject arguments) {
        return String.format("[工具调用] %s", getDisplayName());
    }
}
